#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/vfs.h>    /* or <sys/statfs.h> */
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/hdreg.h>
#include "common.h"
#include "configure.h"
#include "disk_answer.h"

#define DISK_ANSWER_PORT 82

extern struct conf *gconf;

void loadavg(int *av1, int *av5, int *av15)
{
	int fd;
	int nread;
	char buff[2048+1];
	double avg_1 = 0, avg_5 = 0, avg_15 = 0;

	*av1 = *av5 = *av15 = 0;
	
	if ((fd = open("/proc/loadavg", O_RDONLY)) < 0)
		return;

	if ((nread = read(fd, buff, 2048)) >= 0)
	{
		buff[nread] = 0;
		sscanf(buff, "%lf %lf %lf", &avg_1, &avg_5, &avg_15);
		*av1  = avg_1 * 100;
		*av5  = avg_5 *100;
		*av15 = avg_15 * 100;
	}
	close(fd);
	return;
}

void cpu_stat(int *u, int *n, int *s, int *i, int *w, int *x, int *y, int *z)
{
	FILE *fp;
	long long sum;
	char buff[2048+1];
	long long lu = 0, ln = 0, ls = 0, li = 0, lw = 0, lx = 0, ly = 0, lz = 0;

	*u = *n = *s = *i = *w = *x = *y = *z;

	if (!(fp = fopen("/proc/stat", "r")))
		return;

	if (fgets(buff, 2048, fp) > 0)
	{
		sscanf(buff, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
				&lu, &ln, &ls, &li, &lw, &lx, &ly, &lz);
		sum = lu + ln + ls + li + lw + lx + ly + lz;
		if (sum > 0)
		{
			*u = lu*10000/sum;
			*n = ln*10000/sum;
			*s = ls*10000/sum;
			*i = li*10000/sum;
			*w = lw*10000/sum;
			*x = lx*10000/sum;
			*y = ly*10000/sum;
			*z = lz*10000/sum;
		}
	}
	fclose(fp);
}

void meminfo(int *tt, int *fr, int *us, int *bu)
{
	FILE *fp;
	char buff[2048+1];

	*tt = *fr = *us = 0;

	if (!(fp = fopen("/proc/meminfo", "r")))
		return;

	if (fgets(buff, 2048, fp) > 0)
		sscanf(buff, "MemTotal:%d", tt);

	if (fgets(buff, 2048, fp) > 0)
		sscanf(buff, "MemFree:%d", fr);

	if (fgets(buff, 2048, fp) > 0)
		sscanf(buff, "Buffers:%d", bu);

	*us = *tt - *fr;

	fclose(fp);
}

void diskinfo(struct disk *disk, char *file)
{
	int fd;
	struct statfs st;
	struct hd_driveid hd;

	memset(disk, 0, sizeof(struct disk));

	strcpy(disk->model, "unknown");
	strcpy(disk->serial, "unknown");
	strcpy(disk->firmware, "unknown");

	if ((fd = open(file, O_RDONLY)) >= 0)
	{
		if (ioctl(fd, HDIO_GET_IDENTITY, &hd) == 0)
		{
			strcpy(disk->model, hd.model);
			strcpy(disk->serial, hd.serial_no);
			strcpy(disk->firmware, hd.fw_rev);
		}
		close(fd);
	}
}


void fstype(char *dst, int type);
void __partitioninfo(struct partition *p, char *file)
{
	struct statfs st;

	memset(p, 0, sizeof(*p));
	strcpy(p->type, "not specified");
	strcpy(p->mount_point, "not specified");
	if (statfs(file, &st) == 0)
	{
		fstype(p->type, st.f_type);
		strcpy(p->mount_point, file);
		p->total = ((long long)st.f_blocks)*st.f_bsize/1024/1024/1024;
		p->used = ((long long)st.f_blocks  - st.f_bfree + st.f_bavail - st.f_bavail)*st.f_bsize/1024/1024/1024;
		p->free = ((long long)st.f_bavail)*st.f_bsize/1024/1024/1024;
	}
}

void partitioninfo(struct partition p[], char *file[], int num)
{
	int i;
	for (i=0; i<num; i++)
		__partitioninfo(&p[i], file[i]);
}

void pinfo(struct system_info *info)
{
	printf("%d %d\n", info->av1, info->av15);
	printf("%d %d %d %d %d\n", info->us, info->ni, info->sy, info->id, info->wa);
	printf("%d %d %d %d\n", info->mem_tt, info->mem_us, info->mem_fr, info->mem_bu);
	{
		int i;
		for (i=0; i<info->np; i++)
		{
			struct partition *p = &info->p[i];
			printf("%s\n", p->type);
			printf("%s\n", p->mount_point);
			printf("%d %d %d\n", p->total, p->used, p->free);
		}
	}
}

void magic_and_version(struct system_info *system_info)
{
	memcpy(system_info->magic, MAGIC, MAGIC_LEN);
	system_info->version = 4;
}
int collect_info(struct system_info *system_info, struct conf *conf)
{
	memset(system_info, 0, sizeof(*system_info));

	/* magic and version */
	magic_and_version(system_info);

	/* get load avarage */
	loadavg(&system_info->av1, 
			&system_info->av5, 
			&system_info->av15);

	/* get cpu usage */
	cpu_stat(&system_info->us, 
			&system_info->ni, 
			&system_info->sy,
			&system_info->id, 
			&system_info->wa, 
			&system_info->hi, 
			&system_info->si, 
			&system_info->st);

	/* get memory info */
	meminfo(&system_info->mem_tt, 
			&system_info->mem_fr, 
			&system_info->mem_us, 
			&system_info->mem_bu);

	/* get partition info */
	partitioninfo(system_info->p, conf->root_dir, conf->nroot_dir);
	system_info->np = conf->nroot_dir;

	/* get disk info
	   we don't implement it yet, simply set it to zero*/
	system_info->nd = 0;

	/* let reserved be the value of configure option 'space' */
	system_info->reserved = conf->disk_space;

	return 1;
}
void* disk_answer_proc(void *parm)
{
	int sock_lsn;
	int sock_peer;
	struct system_info system_info;
	struct sockaddr_in addr_lsn;

	memset(&addr_lsn, 0, sizeof(addr_lsn));
	addr_lsn.sin_family = AF_INET;
	addr_lsn.sin_port = htons(DISK_ANSWER_PORT);
	addr_lsn.sin_addr.s_addr = htonl(INADDR_ANY);

	if ((sock_lsn = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("socket() error\n");
		_exit(0);
	}

	if (bind(sock_lsn, (struct sockaddr*)&addr_lsn, (socklen_t)sizeof(addr_lsn)) < 0)
	{
		printf("tcp bind() fail\n");
		_exit(0);
	}

	if (listen(sock_lsn, 20) < 0)
	{
		printf("listen error\n");
		_exit(0);
	}
	while (1)
	{
		if ((sock_peer = accept(sock_lsn, 0, 0)) < 0)
		{
			sleep(0);
			continue;
		}
		collect_info(&system_info, gconf);
		send(sock_peer, &system_info, sizeof(system_info), 0);
		close(sock_peer);
	}
	return 0;
}

void start_disk_answer()
{
	pthread_t t;
	if (pthread_create(&t, 0, disk_answer_proc, 0) != 0)
	{
		perror("pthread_create");
		_exit(0);
	}
}


void fstype(char *dst, int type)
{
	switch (type)
	{
		case 0xadf5: 
			strcpy(dst, "ADFS_SUPER_MAGIC"); break;
		case 0xADFF: 
			strcpy(dst, "AFFS_SUPER_MAGIC"); break;
		case 0x42465331: 
			strcpy(dst, "BEFS_SUPER_MAGIC"); break;
		case 0x1BADFACE: 
			strcpy(dst, "BFS_MAGIC"); break;
		case 0xFF534D42: 
			strcpy(dst, "CIFS_MAGIC_NUMBER"); break;
		case 0x73757245: 
			strcpy(dst, "CODA_SUPER_MAGIC"); break;
		case 0x012FF7B7: 
			strcpy(dst, "COH_SUPER_MAGIC"); break;
		case 0x28cd3d45: 
			strcpy(dst, "CRAMFS_MAGIC"); break;
		case 0x1373: 
			strcpy(dst, "DEVFS_SUPER_MAGIC"); break;
		case 0x00414A53: 
			strcpy(dst, "EFS_SUPER_MAGIC"); break;
		case 0x137D: 
			strcpy(dst, "EXT_SUPER_MAGIC"); break;
		case 0xEF51: 
			strcpy(dst, "EXT2_OLD_SUPER_MAGIC"); break;
		case 0xEF52: 
			strcpy(dst, "EXT2_SUPER_MAGIC"); break;
		case 0xEF53: 
			strcpy(dst, "EXT3_SUPER_MAGIC"); break;
		case 0xEF54: 
			strcpy(dst, "EXT4_SUPER_MAGIC"); break;
		case 0x4244: 
			strcpy(dst, "HFS_SUPER_MAGIC"); break;
		case 0xF995E849: 
			strcpy(dst, "HPFS_SUPER_MAGIC"); break;
		case 0x958458f6: 
			strcpy(dst, "HUGETLBFS_MAGIC"); break;
		case 0x9660: 
			strcpy(dst, "ISOFS_SUPER_MAGIC"); break;
		case 0x72b6: 
			strcpy(dst, "JFFS2_SUPER_MAGIC"); break;
		case 0x3153464a: 
			strcpy(dst, "JFS_SUPER_MAGIC"); break;
		case 0x137F : 
			strcpy(dst, "MINIX_SUPER_MAGIC"); break;
		case 0x138F : 
			strcpy(dst, "MINIX_SUPER_MAGIC2"); break;
		case 0x2468 : 
			strcpy(dst, "MINIX2_SUPER_MAGIC"); break;
		case 0x2478 : 
			strcpy(dst, "MINIX2_SUPER_MAGIC2"); break;
		case 0x4d44: 
			strcpy(dst, "MSDOS_SUPER_MAGIC"); break;
		case 0x564c: 
			strcpy(dst, "NCP_SUPER_MAGIC"); break;
		case 0x6969: 
			strcpy(dst, "NFS_SUPER_MAGIC"); break;
		case 0x5346544e: 
			strcpy(dst, "NTFS_SB_MAGIC"); break;
		case 0x9fa1: 
			strcpy(dst, "OPENPROM_SUPER_MAGIC"); break;
		case 0x9fa0: 
			strcpy(dst, "PROC_SUPER_MAGIC"); break;
		case 0x002f: 
			strcpy(dst, "QNX4_SUPER_MAGIC"); break;
		case 0x52654973: 
			strcpy(dst, "REISERFS_SUPER_MAGIC"); break;
		case 0x7275: 
			strcpy(dst, "ROMFS_MAGIC"); break;
		case 0x517B: 
			strcpy(dst, "SMB_SUPER_MAGIC"); break;
		case 0x012FF7B6: 
			strcpy(dst, "SYSV2_SUPER_MAGIC"); break;
		case 0x012FF7B5: 
			strcpy(dst, "SYSV4_SUPER_MAGIC"); break;
		case 0x01021994: 
			strcpy(dst, "TMPFS_MAGIC"); break;
		case 0x15013346: 
			strcpy(dst, "UDF_SUPER_MAGIC"); break;
		case 0x00011954: 
			strcpy(dst, "UFS_MAGIC"); break;
		case 0x9fa2: 
			strcpy(dst, "USBDEVICE_SUPER_MAGIC"); break;
		case 0xa501FCF5: 
			strcpy(dst, "VXFS_SUPER_MAGIC"); break;
		case 0x012FF7B4: 
			strcpy(dst, "XENIX_SUPER_MAGIC"); break;
		case 0x58465342: 
			strcpy(dst, "XFS_SUPER_MAGIC"); break;
		case 0x012FD16D: 
			strcpy(dst, "_XIAFS_SUPER_MAGIC"); break;
		default:
			strcpy(dst, "unknown");
	}
}
